package org.musicguardian.core.scan.remote;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import net.sf.saxon.xpath.XPathEvaluator;

import org.musicguardian.core.Core;
import org.musicguardian.core.model.Album;
import org.musicguardian.core.model.Artist;
import org.musicguardian.core.model.Library;
import org.musicguardian.core.model.RemoteLink;
import org.musicguardian.core.scan.Scanner;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.sun.org.apache.xerces.internal.parsers.DOMParser;
import com.sun.org.apache.xml.internal.serializer.ToUnknownStream;
import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault;
import com.sun.org.apache.xpath.internal.XPathAPI;
import com.sun.org.apache.xpath.internal.objects.XObject;

public class MusicBrainzScanner extends Scanner<List<RemoteLink>> {

    private static final String ARTIST_PATTERN = "%ARTIST%";

    private Artist artistToScan;

    private int musicBrainzScore;

    private static final String TAG_RELEASE = "release";

    private static final String TAG_ARTIST = "artist";

    private static final String TAG_ARTIST_NAME = "name";

    private static final String TAG_ALBUM_NAME = "title";

    private static final String TAG_SCORE = "score";

    private static final String musicBrainzUrl = "http://musicbrainz.org/ws/1/release/?type=xml&artist="
	    + ARTIST_PATTERN + "&releasetypes=Official&limit=100";

    public MusicBrainzScanner(Artist artistToScan, int musicBrainzScore) {
	super();
	this.artistToScan = artistToScan;
	this.musicBrainzScore = musicBrainzScore;
    }

    private List<RemoteLink> scanArtist(Artist localArtist) throws Exception {
	URL url = null;

	try {
	    url = new URL(musicBrainzUrl.replaceAll(ARTIST_PATTERN, localArtist
		    .getName().replaceAll(" ", "+")));
	} catch (MalformedURLException e) {
	    throw new Exception("The URL: " + url
		    + " is invalid, correct the server setting");
	}
	assert url != null;
	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	DocumentBuilder builder = factory.newDocumentBuilder();
	Document document = builder.parse(url.toString());

	NodeList AlbumNodes = getFiltredAlbums(document);
	List<RemoteLink> links = new ArrayList<RemoteLink>();
	for (int i = 0; i < AlbumNodes.getLength(); i++) {
	    Node albumNode = AlbumNodes.item(i);
	    String xpathArtistName = TAG_ARTIST + "/" + TAG_ARTIST_NAME;
	    Artist remoteArtist = new Artist(XPathAPI
		    .eval(albumNode, xpathArtistName).str());
	    String xpathAlbumName = TAG_ALBUM_NAME;
	    remoteArtist.addAlbum(new Album(XPathAPI.eval(albumNode, xpathAlbumName)
		    .str()));
	    String xpathScore = "@"+TAG_SCORE;
	    Integer weight= new Integer(XPathAPI.eval(albumNode, xpathScore ).str());
	    RemoteLink remoteLink = new RemoteLink(weight, localArtist, remoteArtist);
	    links.add(remoteLink);
	    notifyScannerListeners(remoteLink);
	}
	return links;
    }

    private NodeList getFiltredAlbums(Document document)
	    throws TransformerException, XPathExpressionException {
	String string = "//node()[name()='" + TAG_RELEASE + "'][@" + TAG_SCORE
		+ ">='" + musicBrainzScore + "']";
	NodeList selectNodeList = XPathAPI.selectNodeList(document, string);
	return selectNodeList;
    }

    @Override
    public int getTotalWork() {
	return artistToScan.getAlbums().size();
    }

    @Override
    public List<RemoteLink> run() {
	List<RemoteLink> remoteLinks = new ArrayList<RemoteLink>();
	try {
	    remoteLinks = scanArtist(artistToScan);
	} catch (Exception e) {
	    new Exception("Unable to find artist '" + artistToScan.getName()
		    + "'");
	}
	return remoteLinks;
    }

    public static void main(String[] args) throws Exception {
	TreeSet<Library> libraries = Core.getInstance().getModel()
		.getLocalLibraries();
	Artist[] artists = libraries.first().getArtists().toArray(
		new Artist[] {});
	for (int i = 0; i < artists.length; i++) {
	    Artist artist = artists[i];
	    MusicBrainzScanner scanner = new MusicBrainzScanner(artist, 50);
	    List<RemoteLink> links = scanner.run();
	    System.out.println("\nScanned Artist :" + artist);
	    System.out.println("\nFound Artists :" + links);
	}
    }
}
